Jx.declare('Jx.lib.String', {

	$singleton: true,

	__re_Word: /^[a-z]|[^a-z][a-z]/ig,

	__re_TrimLeft: /^[\s\t\r\n ]+/,

	__re_TrimRight: /[\s\t\r\n ]+$/,

	__re_HeaderPairs: /^([^\r\n\:]+)\:(.+)$/,

	__re_EscapeCChar: /[^\u0020-\u0021\u0023-\u0026\u0028-\u007e]/g,

	__re_IdStr: /^(.+)([0-9]+)$/,

	__ref_Escapables: {

		'"': '"',

		"'": "'",

		'/': '/',

		'\\': '\\',

		'\u0008': 'b',

		'\u000c': 'f',

		'\r': 'r',

		'\n': 'n',

		'\u0009': 't'

	},

	__callback_WordReplace: function(m){

		var l = m.length,

			at = l - 1;

		return m.substring(0, at) + m.charAt(at).toUpperCase();

	},

	ids: undefined,

	constructor: function(){

		this.ids = {};

		this.$super();

	},

	createId: function(prefix){

		prefix = Jx.isString(prefix) ? prefix : 'jx-id';

		var list = this.ids,

			o = list.hasOwnProperty(prefix) ?

				list[prefix]

				:

				(list[prefix] = []);

		return prefix + (

			o.length ?

				o.pop()

				:

				(o.gen = (o.gen || 0) + 1)
		);

	},

	removeId: function(id){

		var list = this.ids,

			m = Jx.isString(id) && id.match(this.__re_IdStr),

			prefix, gen, o;

		if (m &&

			list.hasOwnProperty(prefix = m[1]) &&

			(gen = parseInt(m[2], 10)) in (o = list[prefix])

		){

			o.push(gen);

		}

	},

	ucwords: function(str){

		return str.replace(this.__re_Word, this.__callback_WordReplace);

	},

	repeat: function(str, n){

		for(var rep = ''; n--;){

			rep+=str;

		}

		return rep;

	},

	trim: function(str){

		var left = arguments[1],

			l = left === true,

			r = left === false;

		if (l || !r){

			str = str.replace(this.__re_TrimLeft,'');

		}

		if (r || !l){

			str = str.replace(this.__re_TrimRight,'');

		}

		return str;

	},

	pad: function(str, len, chr, right){

		var J = Jx,

			deficit, pad, c, pl;

		if (J.isScalar(str) && J.isNumber(len, 0) && J.isString(chr, true)){

			deficit = len - (str = String(str)).length;

			if (deficit > 0){

				pad = '';

				pl = chr.length;

				c = 0;

				while(deficit--){

					pad += chr.charAt(

						c = (c + 1) % pl

					);

				}

				str = right === true ?

					str + pad

					:

					pad + str;

			}

		}

		return str;

	},

	escape: function(str){

		var my = this,

			esc = this.__ref_Escapables;

		return Jx.isString(str) ?

			str.replace(this.__re_EscapeCChar, function(m){

				var code = m.charCodeAt(0);

				return '\\' + (

					code >= 3 && code <= 126 ?

						(m in esc ? esc[m] : m)

						:

						'u' + my.pad( code.toString(16), 4, '0' )

				);

			})

			:

			'';

	},

	urlEncode: typeof encodeURIComponent == 'undefined' ?

		function(o){ return Jx.isString(o) ? escape(o) : ''; }

		:

		function(o){ return Jx.isString(o) ? encodeURIComponent(o) : ''; },

	urlDecode: typeof decodeURIComponent == 'undefined' ?

		function(o){ return Jx.isString(o) ? unescape(o) : ''; }

		:

		function(o){ return Jx.isString(o) ? decodeURIComponent(o) : ''; },

	parseHeaders: function(headerString){

		var headers = {},

			pairRe = this.__re_HeaderPairs,

			index,

			pairs, pair, name, value, current;

		if (Jx.isString(headerString, true) &&

			(pairs = headerString.split('\n')).length

		){

			for(var c=0,l=pairs.length; c<l; c++){

				pair = this.trim(pairs[c]);

				index = pair.indexOf(':')

				if (index != -1){

					name = this.trim(pair.substring(0, index));

					value = this.trim(pair.substring(index+1, pair.length));

					current = headers.hasOwnProperty(name) ?

						headers[name]

						:

						(headers[name] = []);

					current.push(value);

				} else if (pair && current){

					current[current.length - 1] += ' '+pair;

				}

			}

		}

		return headers;

	}

});
